package logic.nodes;

import gameStates.absGamesStates.AbsIngameState;

import java.util.HashMap;

import settings.Config;

import logic.nodes.collision.CollidableNode;
import logic.nodes.lod.LODNode;
import logic.nodes.lod.TriMeshBoundController;

import com.jme.bounding.BoundingBox;
import com.jme.bounding.BoundingSphere;
import com.jme.bounding.BoundingVolume;
import com.jme.scene.Node;
import com.jme.scene.TriMesh;

public abstract class ModelNode extends Node {
	
	private static final long serialVersionUID = 1L;
	
	private static int index;
	
	private Node model;
	
	protected String path;
	protected int i;
	protected BoundingVolume bound, dodgeBound;
	protected HashMap<TriMesh, TriMeshBoundController> boundControllers;
	protected boolean isLocked;

	protected AbsIngameState ingameState;
	
	public ModelNode(String path, String name, Node model, AbsIngameState ingameState) {
		super(name);
		this.path = path;
		this.ingameState = ingameState;
		
		boundControllers = new HashMap<TriMesh, TriMeshBoundController>();
		
		if(!Config.get().useLOD() || !(this instanceof LODNode)) setModel(model);
		
		i = ++index;
	}
	
	public BoundingVolume getBound() { return bound; }
	
	public final BoundingVolume getBBNodeBound() { return bound; }
	
	public BoundingVolume getDodgeBound() { return dodgeBound; }
	
	public boolean allBoundsDone() { return bound != null && dodgeBound != null; }
	
	public void setBound(BoundingVolume bound) {
		this.bound = bound;
		bound.setCenter(getLocalTranslation());
		
		dodgeBound = bound.clone(null);
		dodgeBound.setCenter(getLocalTranslation());
		
		float dodgeFactor = 1.4f;
		if(dodgeBound instanceof BoundingBox) {
			BoundingBox box = (BoundingBox)dodgeBound;
			box.xExtent *= dodgeFactor;
			box.yExtent *= dodgeFactor;
			box.zExtent *= dodgeFactor;
		} else {
			assert(dodgeBound instanceof BoundingSphere);
			BoundingSphere sphere = (BoundingSphere)dodgeBound;
			sphere.radius *= dodgeFactor;
		}
		
		addToIngameState();
	}
	
	protected void addToIngameState() {
		ingameState.getRootBlock().searchLeafBlocks((CollidableNode)this);
	}
	
	public abstract boolean canMove();
	
	public void lockNode() {
		if(isLocked) return;
		if(boundControllers.isEmpty()) {
			lockThis();
			isLocked = true;
		}
	}
	
	protected void lockThis() {
		if(!canMove()) {
			lockBounds();
			lockShadows();
			lockTransforms();
			lockBranch();
		}
		lockMeshes();
	}
	
	public final Node getModel() { return model; }
	
	public void setModel(Node model) {
		if(this.model != null) detachChild(this.model);
		this.model = model;
		attachChild(model);
		
		if(!allBoundsDone()) {
			setModelBound(new BoundingBox());
			updateModelBound();
			updateWorldBound();
			
			isLocked = false;
			bound = null;
			setModelNodeBoundController();
		}
	}
	
	protected void setModelNodeBoundController() {
		addController(new ModelNodeBoundController(this));
	}
	
	public String getPath() { return path; }
	
	public AbsIngameState getIngameState() { return ingameState; }
		
	@Override
	public int hashCode() { return i; }
}